﻿#include "resource_manager.h"
// Inner Fire 游戏引擎库
// ResourceManager - 资源管理器
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：框架层(5)
// 用于游戏中管理资源载入的项目。贴图、模型的载入都靠这个资源管理器统一管理。
// 总之关于内存的一切，都应该集中到这个管理器内部进行管理。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-04-21

#include "resource_manager.h"
#include <dx/graphics/dx_graphics.h>
#include <game/core/timer.h>
#include <game/manager/game_manager.h>
#include <dx/mesh/dx_mesh_factory.h>

namespace ifire::game {

ifire::game::ResourceManager::ResourceManager(GameManager* game_manager)
    : gm_(game_manager) {}

void ResourceManager::ReadyCommand() {
  dx_ = gm_->GetGraphics();
  assets_ = gm_->GetAssets();

  // 使DX的队列清空，处于预备状态
  dx_->FlushCommandQueue();
  dx_->ResetCommandList();
}

void ResourceManager::CommitCommand() {
  dx_->ExecuteCommands();
  dx_->FlushCommandQueue();
}

void ResourceManager::LoadModel(Model* model) {
  if (model == nullptr) {
    return;
  }
  
  // 如果已经载入，则跳过
  if (model->IsLoaded()) {
    return;
  }

  // 先载入这个Model所需要的材质
  for (auto& e : model->GetMaterials().Items()) {
    LoadMaterial(e.material);
  }

  // 载入
  model->Load(dx_->Device(), dx_->CommandList());
  model->SetLoaded();
}

void ResourceManager::LoadMaterial(Material* material) {
  if (material == nullptr) {
    return;
  }
  // 已经载入，不重复载入
  if (material->IsLoaded()) {
    return;
  }

  // 先载入这个Material所需要的Texture
  if (!material->albedo.empty()) {
    LoadTexture(material->albedo);
  }
  if (!material->normal.empty()) {
    LoadTexture(material->normal);
  }

  dx::MaterialDesc desc;
  desc.name = material->name;
  desc.albedo = material->albedo;
  desc.normal = material->normal;
  desc.tint = material->tint;
  desc.fresnel_r0 = material->fresnel_r0;
  desc.roughness = material->roughness;
  auto item = CreateMaterial(desc);
  material->SetMaterial(item);
  material->SetLoaded();
}

void ResourceManager::LoadMaterial(ikit::sstr material_name) {
  if (material_name.empty()){
    return;
  }

  auto material = gm_->GetAssets()->GetMaterial(material_name);
  if (material == nullptr) {
    ikit::error("Unable find Material: %s", material_name.c_str());
  }

  LoadMaterial(material);
}

void ResourceManager::LoadTexture(ikit::SpritePack* sprite_pack) {
  // 创建资源
  auto dx_texture = std::make_unique<dx::DxTexture>(
      0, sprite_pack->name, dx::TextureType::Image);
  // 从ImageData中创建贴图
  dx_texture->CreateFromImage(
      dx_->Device(), dx_->CommandList(), sprite_pack->img);

  // 为资源建立一个描述符。此资源的index将会被设置的描述符的对应位置。
  dx_->SetTextureDescriptor(dx_texture.get());


  // 设置Texture到Assets资源
  // texture->SetTexture(dx_texture.get());

  auto ptr = dx_texture.get();
  textures_.push_back(std::move(dx_texture));
}

void ResourceManager::LoadTexture(Texture* texture) {
  if (texture == nullptr) {
    return;
  }
  // 已经载入，不重复载入
  if (texture->IsLoaded()) {
    return;
  }

  // 创建一个贴图
  auto dx_texture =
      std::make_unique<dx::DxTexture>(0, texture->name, texture->Type());
  // 载入贴图
  dx_texture->CreateFromFile(dx_->Device(), dx_->CommandList(),
      texture->Filename(), texture->IsFilp());

  // 为资源建立一个描述符。此资源的index将会被设置的描述符的对应位置。
  dx_->SetTextureDescriptor(dx_texture.get());


  // 设置Texture到Assets资源
  texture->SetTexture(dx_texture.get());

  auto ptr = dx_texture.get();
  textures_.push_back(std::move(dx_texture));

  texture->SetLoaded();
}

void ResourceManager::LoadTexture(ikit::sstr texture_name) {
  auto texture = gm_->GetAssets()->GetTexture(texture_name);
  if (texture == nullptr) {
    ikit::error("Unable find texture: %s", texture_name.c_str());
  }
  LoadTexture(texture);
}

int ResourceManager::FindTextureIndex(ikit::sstr name) {
  auto ptr = FindTexture(name);
  return ptr == nullptr ? -1 : ptr->Index();
}

dx::DxTexture* ResourceManager::FindTexture(ikit::sstr name) {
  // 如果是空字符串，直接返回-1
  if (name.empty()) {
    return nullptr;
  }
  for (auto& e : textures_) {
    if (e->Name() == name) {
      return e.get();
    }
  }
  KERROR("Unable find texture: %s", name.c_str());
  return nullptr;
}

void ResourceManager::SetupSystemResource() {
  draw_desc_.ui_sprite_index = FindTextureIndex("UISprite");

  font_material_ = FindMaterial("SYS_FONT");
  draw_desc_.font_index = font_material_->diffuse_index;

  water_material_ = FindMaterial("SYS_WATER");

  // 创建默认的天空盒
  draw_desc_.sky_box = FindTexture("SYS_SKY_BOX");
  // 得到空白法线
  draw_desc_.empty_normal = FindTexture("SYS_EMPTY_NORMAL");
  draw_desc_.empty_normal_index = draw_desc_.empty_normal->Index();

  // 创建一个空MESH
  draw_desc_.empty_mesh = dx::DxMeshFactory::GetInstance().GetUIQuad();

  draw_desc_.items = &items_;
  draw_desc_.render_layers = render_layer_;
  draw_desc_.materials = &materials_;
  draw_desc_.animators = &animators_;

  dx_->SetDrawDesc(draw_desc_);
}

dx::Material* ResourceManager::CreateMaterial(const dx::MaterialDesc& desc) {
  auto item = std::make_unique<dx::Material>(material_index_.allocate(), desc);

  item->diffuse_index = FindTextureIndex(item->albedo);
  if (item->diffuse_index == -1) {
    item->diffuse_index = FindTextureIndex("SYS_DEFAULT_DIFFUSE");
  }
  item->normal_index = FindTextureIndex(item->normal);

  auto ptr = item.get();
  materials_.push_back(std::move(item));
  return ptr;
}

dx::Material* ResourceManager::FindMaterial(ikit::sstr name) {
  // 如果是空字符串，直接返回-1
  if (name.empty()) {
    return nullptr;
  }

  for (auto& e : materials_) {
    if (e->Name() == name) {
      return e.get();
    }
  }

  KERROR("[ResourceManager]Unable find material: %s", name.c_str());
  return nullptr;
}

dx::Material* ResourceManager::GetMaterial(int index) {
  size_t count = 0;
  for (const auto& element : materials_) {
    if (count == index) {
      return element.get();
    }
    ++count;
  }
  // 如果索引超出范围，返回nullptr
  return nullptr;
}

void ResourceManager::SetToLayer(dx::RenderLayer layer, dx::RenderItem* item) {
  item->render_layer = layer;
  render_layer_[(int)layer].push_back(item);
}

void ResourceManager::AnimateWaterMaterials() {
  // Scroll the water material texture coordinates.
  if (water_material_ == nullptr) {
    return;
  }

  float& tu = water_material_->mat_transform(3, 0);
  float& tv = water_material_->mat_transform(3, 1);

  tu += 0.1f * g_timer.fixedDeltaTime;
  tv += 0.02f * g_timer.fixedDeltaTime;

  if (tu >= 1.0f)
    tu -= 1.0f;

  if (tv >= 1.0f)
    tv -= 1.0f;

  water_material_->mat_transform(3, 0) = tu;
  water_material_->mat_transform(3, 1) = tv;

  // Material has changed, so need to update cbuffer.
  water_material_->frame_dirty_count = FRAME_RESOURCE_COUNT;
}

dx::RenderItem* ResourceManager::CreateRenderItem(
    dx::RenderLayer layer, bool is_skin_mesh) {
  auto item = std::make_unique<dx::RenderItem>();

  // 分配给此项目索引（重要！顺序不能乱了！）
  item->cb_index = static_cast<UINT>(render_item_index_.allocate());

  // 如果是SkinMesh，需要修改特定的项目
  if (is_skin_mesh) {
    if (layer == dx::RenderLayer::Opaque) {
      layer = dx::RenderLayer::SkinnedOpaque;
    } else if (layer == dx::RenderLayer::Transparent) {
      layer = dx::RenderLayer::SkinnedTransparent;
    }  else if (layer == dx::RenderLayer::AlphaTransparent) {
      layer = dx::RenderLayer::SkinnedAlpha;
    } else if (layer == dx::RenderLayer::AlphaTest) {
      layer = dx::RenderLayer::SkinnedAlphaTest;
    }
  }

  auto ptr = item.get();
  SetToLayer(layer, ptr);
  items_.push_back(std::move(item));
  return ptr;
}

dx::SkinAnimator* ResourceManager::CreateAnimator() {
  // 分配索引
  int index = static_cast<int>(animator_index_.allocate());

  auto item = std::make_unique<dx::SkinAnimator>(index);

  auto ptr = item.get();
  animators_.push_back(std::move(item));

  return ptr;
}

} // namespace ifire::game